#pragma once
#include <strmif.h>
#include <string>
#include "vtsreadoutpin.hpp"
#include <vector>

namespace VTSRead
{

//HRESULT CreateInstance(
//    IClassFactory*,
//    IUnknown*, 
//    const IID&, 
//    void**);

class Filter : public IBaseFilter, public IFileSourceFilter
{
    friend HRESULT CreateInstance(
            IClassFactory*,
            IUnknown*, 
            const IID&, 
            void**);
    
    Filter(IClassFactory*, IUnknown*);
    virtual ~Filter();
    
    Filter(const Filter&);
    Filter& operator=(const Filter&);
    
public:

    //IUnknown

    HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**);
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();
    
    //IBaseFilter

    HRESULT STDMETHODCALLTYPE GetClassID(CLSID*);    
    HRESULT STDMETHODCALLTYPE Stop();    
    HRESULT STDMETHODCALLTYPE Pause();    
    HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME);    
    HRESULT STDMETHODCALLTYPE GetState(DWORD, FILTER_STATE*);    
    HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock*);
    HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock**);
    HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins**);    
    HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR, IPin**);    
    HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO*);    
    HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph*, LPCWSTR);    
    HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR*);

    //IFileSourceFilter
    
    HRESULT STDMETHODCALLTYPE Load(LPCOLESTR, const AM_MEDIA_TYPE*);    
    HRESULT STDMETHODCALLTYPE GetCurFile(LPOLESTR*, AM_MEDIA_TYPE*);
    
    //local classes and methods

    class Lock
    {
        Lock(const Lock&);
        Lock& operator=(const Lock&);
    public:
        Lock();
        ~Lock();

        HRESULT Seize(Filter*, DWORD timeout = 5000);
        void Release();
    private:
        HANDLE m_hMutex;
    };
    
    long Read(__int64, long, BYTE*);

private:

    class nondelegating_t : public IUnknown
    {
    public:
    
        Filter* const m_pFilter;
        LONG m_cRef;
        
        nondelegating_t(Filter*);
        virtual ~nondelegating_t();
        
        HRESULT STDMETHODCALLTYPE QueryInterface(const IID&, void**);
        ULONG STDMETHODCALLTYPE AddRef();
        ULONG STDMETHODCALLTYPE Release();

    private:

        nondelegating_t(const nondelegating_t&);
        nondelegating_t& operator=(const nondelegating_t&);

    };
    
    IClassFactory* const m_pClassFactory;
    nondelegating_t m_nondelegating;
    IUnknown* const m_pOuter;  //decl must follow m_nondelegating
    REFERENCE_TIME m_start;
    IReferenceClock* m_clock;
    //IFilterGraph* m_pGraph;
    //std::wstring m_name;
    FILTER_INFO m_info;
    FILTER_STATE m_state;
    
    std::wstring m_filename;  //the one passed to load
    Outpin m_outpin;
    
    HANDLE m_hMutex;
    
public:

    struct FileInfo
    {
        std::wstring m_path;
        __int64 m_pos;  //relative to total pseudo-file
        __int64 m_size;
        HANDLE m_hFile;
        
        FileInfo();
        //FileInfo(const WIN32_FIND_DATA&);

        FileInfo(const FileInfo&);
        
        //FileInfo& operator=(const FileInfo&);
        //FileInfo& operator=(const WIN32_FIND_DATA&);
    };
    
    typedef std::vector<FileInfo> files_t;
    files_t m_files;
    
    struct ub_pred_t
    {
        bool operator()(const FileInfo& f, __int64 pos) const
        {
            return (f.m_pos < pos);
        }
        
        bool operator()(__int64 pos, const FileInfo& f) const
        {
            return (pos < f.m_pos);
        }
        
        bool operator()(const FileInfo& lhs, const FileInfo& rhs) const
        {
            return (lhs.m_pos < rhs.m_pos);
        }
    };
    
    __int64 m_length;  //total length of pseudo-file    

};

}  //end VTSRead